﻿using System;
using System.ServiceModel;
using System.Windows.Forms;
using CC.Utilities;
using CC.Yacht.Client.YachtClient;
using CC.Yacht.Messages;
using CC.Yacht.Windows.Forms;

namespace CC.Yacht.Client
{
    public partial class FormClient : Form, IYachtServiceCallback
    {
        #region Constructor
        public FormClient()
        {
            InitializeComponent();
            Icon = Yacht.Properties.Resources.Die;
            
            base.Text = Application.ProductName;

            if (!DesignMode)
            {
                _LoginForm.Authenticating += _LoginForm_Authenticating;
                _LoginForm.Registering += _LoginForm_Registering;
                _YachtServiceClient = new YachtServiceClient(new InstanceContext(this));
            }
        }
        #endregion

        #region Private Fields
        private readonly LoginForm _LoginForm = new LoginForm();
        private Guid _SessionId = Guid.Empty;
        private YachtServiceClient _YachtServiceClient;
        #endregion

        #region Private Event Handlers
        private void _GamePanel_DiceChanged(object sender, DiceChangedEventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.UpdateDice(_SessionId, _GamePanel.GameId, e.Dice);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _GamePanel_Rolling(object sender, EventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.Roll(_SessionId, _GamePanel.GameId);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _GamePanel_ScoreChanged(object sender, ScoreChangedEventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.SetScoreField(_SessionId, _GamePanel.GameId, e.Field);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _GamePanel_SendingMessage(object sender, SendMessageEventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.SendMessage(_SessionId, Target.Game, _GamePanel.GameId, e.Message);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _GamePanel_StartingGame(object sender, EventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.StartGame(_SessionId, _GamePanel.GameId);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _LeaveGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DialogResult.Yes == MessageBox.Show("Are you sure you want to leave the game?", Application.ProductName + " - Leave Game", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
            {
                OpenClient();

                try
                {
                    _YachtServiceClient.LeaveGame(_SessionId, _GamePanel.GameId);

                    _LeaveGameToolStripMenuItem.Visible = false;
                    _LobbyPanel.BringToFront();
                }
                catch (CommunicationException exception)
                {
                    HandleCommunicationException(exception);
                }
            }
        }

        private void _LobbyPanel_CreatingGame(object sender, EventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.CreateGame(_SessionId, _LobbyPanel.LobbyId);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _LobbyPanel_JoiningGame(object sender, JoinGameEventArgs e)
        {
            OpenClient();

            try
            {
                _YachtServiceClient.JoinGame(_SessionId, e.GameId);
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
            }
        }

        private void _LobbyPanel_SendingMessage(object sender, SendMessageEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.Message))
            {
                OpenClient();

                try
                {
                    _YachtServiceClient.SendMessage(_SessionId, Target.Lobby, _LobbyPanel.LobbyId, e.Message);
                }
                catch (CommunicationException exception)
                {
                    HandleCommunicationException(exception);
                }
            }
        }

        // ReSharper disable InconsistentNaming
        private void _LoginForm_Authenticating(object sender, AuthenticateEventArgs e)
        {
            bool loggedIn;
            OpenClient();

            try
            {
                var loginResponse = _YachtServiceClient.Login(e.Email, e.Password);

                if (!loginResponse.IsError && loginResponse.IsLoggedIn)
                {
                    _SessionId = loginResponse.SessionId;

                    _GamePanel.Reset();
                    _GamePanel.User = loginResponse.User;

                    _LobbyPanel.Reset();
                    _LobbyPanel.User = loginResponse.User;

                    _LobbyPanel.Enabled = false;

                    if (loginResponse.GameId != Guid.Empty)
                    {
                        _YachtServiceClient.JoinGame(_SessionId, loginResponse.GameId);
                    }
                    else
                    {
                        _YachtServiceClient.JoinLobby(_SessionId, Guid.Empty);
                    }

                    UpdateLoginLogoutText();
                    loggedIn = true;
                }
                else
                {
                    ShowErrorMessage("Error logging in:\r\n\r\nPlease verify your username and password.", "Error logging in", MessageBoxIcon.Error);
                    loggedIn = false;
                }
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
                loggedIn = false;
            }

            _LoginForm.EndAuthenticate(loggedIn);
        }

        private void _LoginForm_Registering(object sender, RegisterEventArgs e)
        {
            bool registeredSuccessfully;
            OpenClient();

            try
            {
                var registerResponse = _YachtServiceClient.Register(e.Email, e.Username, e.Password);

                if (registerResponse.IsError)
                {
                    string errorType = registerResponse.ErrorType.ToStringEx();
                    string errorMessage = "An error occurred while registering" + (!string.IsNullOrEmpty(errorType) ? ":\r\n\r\n" + errorType : ".");

                    ShowErrorMessage(errorMessage, "Error Registering", MessageBoxIcon.Error);
                    registeredSuccessfully = false;
                }
                else
                {
                    registeredSuccessfully = true;
                }
            }
            catch (CommunicationException exception)
            {
                HandleCommunicationException(exception);
                registeredSuccessfully = false;
            }

            _LoginForm.EndRegister(registeredSuccessfully);
        }
        // ReSharper restore InconsistentNaming

        private void _LoginToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_SessionId == Guid.Empty)
            {
                _LoginForm.ShowDialog();
            }
            else
            {
                if (DialogResult.Yes == MessageBox.Show("Are you sure you want to logout?", Application.ProductName + " - Logout", MessageBoxButtons.YesNo, MessageBoxIcon.Question))
                {
                    OpenClient();

                    try
                    {
                        _YachtServiceClient.Logout(_SessionId);

                        _LobbyPanel.BringToFront();
                        _LobbyPanel.Reset();
                        _LobbyPanel.Enabled = false;
                        _GamePanel.Reset();
                        _SessionId = Guid.Empty;

                        UpdateLoginLogoutText();
                    }
                    catch (CommunicationException exception)
                    {
                        HandleCommunicationException(exception);
                    }
                }
            }
        }

        private void FormClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_YachtServiceClient != null && _SessionId != Guid.Empty)
            {
                OpenClient();

                try
                {
                    _YachtServiceClient.Logout(_SessionId);
                }
                catch (CommunicationException exception)
                {
                    HandleCommunicationException(exception);
                }
            }
        }
        #endregion

        #region Private Methods
        private void HandleCommunicationException(CommunicationException exception)
        {
            Logging.LogException(exception);
#if DEBUG
            ShowErrorMessage("Error communicating to server:\r\n\r\n" + exception, "Error communicating to server", MessageBoxIcon.Error);
#else
            ShowErrorMessage("Error communicating to server:\r\n\r\n" + exception.Message, "Error communicating to server", MessageBoxIcon.Error);
#endif
            _YachtServiceClient = null;
        }

        private void OpenClient()
        {
            if (_YachtServiceClient != null)
            {
                switch (_YachtServiceClient.State)
                {
                    case CommunicationState.Closed:
                    case CommunicationState.Closing:
                    case CommunicationState.Faulted:
                        {
                            _YachtServiceClient = null;
                            break;
                        }
                }
            }

            if (_YachtServiceClient == null)
            {
                _YachtServiceClient = new YachtServiceClient(new InstanceContext(this));
            }
        }

        private static void ShowErrorMessage(string message, string title, MessageBoxIcon icon)
        {
            MessageBox.Show(message, Application.ProductName + " - " + title, MessageBoxButtons.OK, icon);
        }

        private void UpdateLoginLogoutText()
        {
            _LoginToolStripMenuItem.Text = (_SessionId == Guid.Empty) ? "&Login" : "&Logout";
        }
        #endregion

        #region IYachtServiceCallback Members
        public void GameMessageReceived(User user, string message)
        {
            _GamePanel.MessageReceived(user, message);
        }

        public void GameRolled(Dice dice)
        {
            _GamePanel.Rolled(dice);
        }

        public void GameStateChanged(GameState gameState)
        {
            _GamePanel.SetGameState(gameState);
        }

        public void GameOver(User winner)
        {
            _GamePanel.GameOver(winner);
        }

        public void JoinedGame(JoinGameResponse response)
        {
            if (response != null && !response.IsError)
            {
                _LeaveGameToolStripMenuItem.Visible = true;
                _GamePanel.Reset();
                _GamePanel.GameId = response.GameState.GameId;
                _GamePanel.SetGameState(response.GameState);
                _GamePanel.Enabled = true;
                _GamePanel.BringToFront();
            }
            else
            {
                throw new NotImplementedException("Some error handling would be nice...");
            }
        }

        public void JoinedLobby(JoinLobbyResponse response)
        {
            if (response != null && !response.IsError)
            {
                _LobbyPanel.LobbyId = response.LobbyId;
                _LobbyPanel.SetGames(response.Games);
                _LobbyPanel.SetUsers(response.Users);
                _LobbyPanel.Enabled = true;
                _LobbyPanel.BringToFront();
            }
            else
            {
                _LobbyPanel.Enabled = false;
            }
        }

        public void LobbyGameAdded(Game game)
        {
            _LobbyPanel.GameAdded(game);
        }

        public void LobbyGameChanged(Game game)
        {
            _LobbyPanel.GameChanged(game);
        }

        public void LobbyGameRemoved(Game game)
        {
            _LobbyPanel.GameRemoved(game);
        }

        public void LobbyMessageReceived(User user, string message)
        {
            _LobbyPanel.MessageReceived(user, message);
        }

        public void LobbyUserJoined(User user)
        {
            _LobbyPanel.UserJoined(user);
        }

        public void LobbyUserLeft(User user)
        {
            _LobbyPanel.UserLeft(user);
        }
        #endregion
    }
}
